home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / source.exe / POSIX / ELVIS / BLK.C < prev    next >
C/C++ Source or Header  |  1992-10-01  |  10KB  |  471 lines

  1. /* blk.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains the functions that get/put blocks from the temp file.
  12.  * It also contains the "do" and "undo" functions.
  13.  */
  14.  
  15. #include "config.h"
  16. #include "vi.h"
  17.  
  18. #ifndef NBUFS
  19. # define NBUFS    5        /* must be at least 3 -- more is better */
  20. #endif
  21.  
  22. extern long lseek();
  23.  
  24. /*------------------------------------------------------------------------*/
  25.  
  26. BLK        hdr;        /* buffer for the header block */
  27.  
  28. static int    b4cnt;        /* used to count context of beforedo/afterdo */
  29. static struct _blkbuf
  30. {
  31.     BLK        buf;        /* contents of a text block */
  32.     unsigned short    logical;    /* logical block number */
  33.     int        dirty;        /* must the buffer be rewritten? */
  34. }
  35.         blk[NBUFS],    /* buffers for text[?] blocks */
  36.         *toonew,    /* buffer which shouldn't be recycled yet */
  37.         *newtoo,    /* another buffer which should be recycled */
  38.         *recycle = blk;    /* next block to be recycled */
  39.  
  40.  
  41.  
  42.  
  43.  
  44. /* This function wipes out all buffers */
  45. void blkinit()
  46. {
  47.     int    i;
  48.  
  49.     for (i = 0; i < NBUFS; i++)
  50.     {
  51.         blk[i].logical = 0;
  52.         blk[i].dirty = FALSE;
  53.     }
  54.     for (i = 0; i < MAXBLKS; i++)
  55.     {
  56.         hdr.n[i] = 0;
  57.     }
  58. }
  59.  
  60. /* This function allocates a buffer and fills it with a given block's text */
  61. BLK *blkget(logical)
  62.     int    logical;    /* logical block number to fetch */
  63. {
  64.     REG struct _blkbuf    *this;    /* used to step through blk[] */
  65.     REG int    i;
  66.  
  67.     /* if logical is 0, just return the hdr buffer */
  68.     if (logical == 0)
  69.     {
  70.         return &hdr;
  71.     }
  72.  
  73.     /* see if we have that block in mem already */
  74.     for (this = blk; this < &blk[NBUFS]; this++)
  75.     {
  76.         if (this->logical == logical)
  77.         {
  78.             newtoo = toonew;
  79.             toonew = this;
  80.             return &this->buf;
  81.         }
  82.     }
  83.  
  84.     /* choose a block to be recycled */
  85.     do
  86.     {
  87.         this = recycle++;
  88.         if (recycle == &blk[NBUFS])
  89.         {
  90.             recycle = blk;
  91.         }
  92.     } while (this == toonew || this == newtoo);
  93.  
  94.     /* if it contains a block, flush that block */
  95.     blkflush(this);
  96.  
  97.     /* fill this buffer with the desired block */
  98.     this->logical = logical;
  99.     if (hdr.n[logical])
  100.     {
  101.         /* it has been used before - fill it from tmp file */
  102.         lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
  103.         if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
  104.         {
  105.             msg("Error reading back from tmp file!");
  106.         }
  107.     }
  108.     else
  109.     {
  110.         /* it is new - zero it */
  111.         for (i = 0; i < BLKSIZE; i++)
  112.         {
  113.             this->buf.c[i] = 0;
  114.         }
  115.     }
  116.  
  117.     /* This isn't really a change, but it does potentially invalidate
  118.      * the kinds of shortcuts that the "changes" variable is supposed
  119.      * to protect us from... so count it as a change.
  120.      */
  121.     changes++;
  122.  
  123.     /* mark it as being "not dirty" */
  124.     this->dirty = 0;
  125.  
  126.     /* return it */
  127.     newtoo = toonew;
  128.     toonew = this;
  129.     return &this->buf;
  130. }
  131.  
  132.  
  133.  
  134. /* This function writes a block out to the temporary file */
  135. void blkflush(this)
  136.     REG struct _blkbuf    *this;    /* the buffer to flush */
  137. {
  138.     long        seekpos;    /* seek position of the new block */
  139.     unsigned short    physical;    /* physical block number */
  140.  
  141.     /* if its empty (an orphan blkadd() maybe?) then make it dirty */
  142.     if (this->logical && !*this->buf.c)
  143.     {
  144.         blkdirty(&this->buf);
  145.     }
  146.  
  147.     /* if it's an empty buffer or a clean version is on disk, quit */
  148.     if (!this->logical || hdr.n[this->logical] && !this->dirty)
  149.     {
  150.         return;
  151.     }
  152.  
  153.     /* find a free place in the file */
  154. #ifndef NO_RECYCLE
  155.     seekpos = allocate();
  156.     lseek(tmpfd, seekpos, 0);
  157. #else
  158.     seekpos = lseek(tmpfd, 0L, 2);
  159. #endif
  160.     physical = seekpos / BLKSIZE;
  161.  
  162.     /* put the block there */
  163.     if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
  164.     {
  165.         msg("Trouble writing to tmp file");
  166.     }
  167.     this->dirty = FALSE;
  168.  
  169.     /* update the header so it knows we put it there */
  170.     hdr.n[this->logical] = physical;
  171. }
  172.  
  173.  
  174. /* This function sets a block's "dirty" flag or deletes empty blocks */
  175. void blkdirty(bp)
  176.     BLK    *bp;    /* buffer returned by blkget() */
  177. {
  178.     REG int        i, j;
  179.     REG char    *scan;
  180.     REG int        k;
  181.  
  182.     /* find the buffer */
  183.     for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
  184.     {
  185.     }
  186. #ifdef DEBUG
  187.     if (i >= NBUFS)
  188.     {
  189.         msg("blkdirty() called with unknown buffer at 0x%lx", bp);
  190.         return;
  191.     }
  192.     if (blk[i].logical == 0)
  193.     {
  194.         msg("blkdirty called with freed buffer");
  195.         return;
  196.     }
  197. #endif
  198.  
  199.     /* if this block ends with line# INFINITY, then it must have been
  200.      * allocated unnecessarily during tmpstart().  Forget it.
  201.      */
  202.     if (lnum[blk[i].logical] == INFINITY)
  203.     {
  204. #ifdef DEBUG
  205.         if (blk[i].buf.c[0])
  206.         {
  207.             msg("bkldirty called with non-empty extra BLK");
  208.         }
  209. #endif
  210.         blk[i].logical = 0;
  211.         blk[i].dirty = FALSE;
  212.         return;
  213.     }
  214.  
  215.     /* count lines in this block */
  216.     for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
  217.     {
  218.         if (*scan == '\n')
  219.         {
  220.             j++;
  221.         }
  222.     }
  223.  
  224.     /* adjust lnum, if necessary */
  225.     k = blk[i].logical;
  226.     j += (lnum[k - 1] - lnum[k]);
  227.     if (j != 0)
  228.     {
  229.         nlines += j;
  230.         while (k < MAXBLKS && lnum[k] != INFINITY)
  231.         {
  232.             lnum[k++] += j;
  233.         }
  234.     }
  235.  
  236.     /* if it still has text, mark it as dirty */
  237.     if (*bp->c)
  238.     {
  239.         blk[i].dirty = TRUE;
  240.     }
  241.     else /* empty block, so delete it */
  242.     {
  243.         /* adjust the cache */
  244.         k = blk[i].logical;
  245.         for (j = 0; j < NBUFS; j++)
  246.         {
  247.             if (blk[j].logical >= k)
  248.             {
  249.                 blk[j].logical--;
  250.             }
  251.         }
  252.  
  253.         /* delete it from hdr.n[] and lnum[] */
  254.         blk[i].logical = 0;
  255.         blk[i].dirty = FALSE;
  256.         while (k < MAXBLKS - 1)
  257.         {
  258.             hdr.n[k] = hdr.n[k + 1];
  259.             lnum[k] = lnum[k + 1];
  260.             k++;
  261.         }
  262.         hdr.n[MAXBLKS - 1] = 0;
  263.         lnum[MAXBLKS - 1] = INFINITY;
  264.     }
  265. }
  266.  
  267.  
  268. /* insert a new block into hdr, and adjust the cache */
  269. BLK *blkadd(logical)
  270.     int    logical;    /* where to insert the new block */
  271. {
  272.     REG int    i;
  273.  
  274.     /* adjust hdr and lnum[] */
  275.     for (i = MAXBLKS - 1; i > logical; i--)
  276.     {
  277.         hdr.n[i] = hdr.n[i - 1];
  278.         lnum[i] = lnum[i - 1];
  279.     }
  280.     hdr.n[logical] = 0;
  281.     lnum[logical] = lnum[logical - 1];
  282.  
  283.     /* adjust the cache */
  284.     for (i = 0; i < NBUFS; i++)
  285.     {
  286.         if (blk[i].logical >= logical)
  287.         {
  288.             blk[i].logical++;
  289.         }
  290.     }
  291.  
  292.     /* return the new block, via blkget() */
  293.     return blkget(logical);
  294. }
  295.  
  296.  
  297. /* This function forces all dirty blocks out to disk */
  298. void blksync()
  299. {
  300.     int    i;
  301.  
  302.     for (i = 0; i < NBUFS; i++)
  303.     {
  304.         /* blk[i].dirty = TRUE; */
  305.         blkflush(&blk[i]);
  306.     }
  307.     if (*o_sync)
  308.     {
  309. #ifndef DF_POSIX
  310.         sync();
  311. #endif
  312.     }
  313. }
  314.  
  315. /*------------------------------------------------------------------------*/
  316.  
  317. static MARK    undocurs;    /* where the cursor should go if undone */
  318. static long    oldnlines;
  319. static long    oldlnum[MAXBLKS];
  320.  
  321.  
  322. /* This function should be called before each command that changes the text.
  323.  * It defines the state that undo() will reset the file to.
  324.  */
  325. void beforedo(forundo)
  326.     int        forundo;    /* boolean: is this for an undo? */
  327. {
  328.     REG int        i;
  329.     REG long    l;
  330.  
  331.     /* if this is a nested call to beforedo, quit! Use larger context */
  332.     if (b4cnt++ > 0)
  333.     {
  334.         return;
  335.     }
  336.  
  337.     /* force all block buffers to disk */
  338.     blksync();
  339.  
  340. #ifndef NO_RECYCLE
  341.     /* perform garbage collection on blocks from tmp file */
  342.     garbage();
  343. #endif
  344.  
  345.     /* force the header out to disk */
  346.     lseek(tmpfd, 0L, 0);
  347.     if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
  348.     {
  349.         msg("Trouble writing header to tmp file ");
  350.     }
  351.  
  352.     /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
  353.     if (forundo)
  354.     {
  355.         for (i = 0; i < MAXBLKS; i++)
  356.         {
  357.             l = lnum[i];
  358.             lnum[i] = oldlnum[i];
  359.             oldlnum[i] = l;
  360.         }
  361.         l = nlines;
  362.         nlines = oldnlines;
  363.         oldnlines = l;
  364.     }
  365.     else
  366.     {
  367.         for (i = 0; i < MAXBLKS; i++)
  368.         {
  369.             oldlnum[i] = lnum[i];
  370.         }
  371.         oldnlines = nlines;
  372.     }
  373.  
  374.     /* save the cursor position */
  375.     undocurs = cursor;
  376.  
  377.     /* upon return, the calling function continues and makes changes... */
  378. }
  379.  
  380. /* This function marks the end of a (nested?) change to the file */
  381. void afterdo()
  382. {
  383.     if (--b4cnt)
  384.     {
  385.         /* after abortdo(), b4cnt may decribe nested beforedo/afterdo
  386.          * pairs incorrectly.  If it is decremented to often, then
  387.          * keep b4cnt sane but don't do anything else.
  388.          */
  389.         if (b4cnt < 0)
  390.             b4cnt = 0;
  391.  
  392.         return;
  393.     }
  394.  
  395.     /* make sure the cursor wasn't left stranded in deleted text */
  396.     if (markline(cursor) > nlines)
  397.     {
  398.         cursor = MARK_LAST;
  399.     }
  400.     /* NOTE: it is still possible that markidx(cursor) is after the
  401.      * end of a line, so the Vi mode will have to take care of that
  402.      * itself */
  403.  
  404.     /* if a significant change has been made to this file, then set the
  405.      * MODIFIED flag.
  406.      */
  407.     if (significant)
  408.     {
  409.         setflag(file, MODIFIED);
  410.     }    
  411. }
  412.  
  413. /* This function cuts short the current set of changes.  It is called after
  414.  * a SIGINT.
  415.  */
  416. void abortdo()
  417. {
  418.     /* finish the operation immediately. */
  419.     if (b4cnt > 0)
  420.     {
  421.         b4cnt = 1;
  422.         afterdo();
  423.     }
  424.  
  425.     /* in visual mode, the screen is probably screwed up */
  426.     if (mode == MODE_COLON)
  427.     {
  428.         mode = MODE_VI;
  429.     }
  430.     if (mode == MODE_VI)
  431.     {
  432.         redraw(MARK_UNSET, FALSE);
  433.     }
  434. }
  435.  
  436. /* This function discards all changes made since the last call to beforedo() */
  437. int undo()
  438. {
  439.     BLK        oldhdr;
  440.  
  441.     /* if beforedo() has never been run, fail */
  442.     if (!tstflag(file, MODIFIED))
  443.     {
  444.         msg("You haven't modified this file yet.");
  445.         return FALSE;
  446.     }
  447.  
  448.     /* read the old header form the tmp file */
  449.     lseek(tmpfd, 0L, 0);
  450.     if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
  451.     {
  452.         msg("Trouble rereading the old header from tmp file");
  453.     }
  454.  
  455.     /* "do" the changed version, so we can undo the "undo" */
  456.     cursor = undocurs;
  457.     beforedo(TRUE);
  458.     afterdo();
  459.  
  460.     /* wipe out the block buffers - we can't assume they're correct */
  461.     blkinit();
  462.  
  463.     /* use the old header -- and therefore the old text blocks */
  464.     hdr = oldhdr;
  465.  
  466.     /* This is a change */
  467.     changes++;
  468.  
  469.     return TRUE;
  470. }
  471.